package org.neogenesis.web.dao;

import gov.nih.nlm.ncbi.www.soap.eutils.EUtilsServiceStub;
import gov.nih.nlm.ncbi.www.soap.eutils.EUtilsServiceStub.DocSumType;
import gov.nih.nlm.ncbi.www.soap.eutils.EUtilsServiceStub.ESearchRequest;
import gov.nih.nlm.ncbi.www.soap.eutils.EUtilsServiceStub.ESearchResult;
import gov.nih.nlm.ncbi.www.soap.eutils.EUtilsServiceStub.IdListType;
import gov.nih.nlm.ncbi.www.soap.eutils.EUtilsServiceStub.ItemType;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;

import org.apache.axis2.AxisFault;
import org.neogenesis.web.loader.MetaProbe;
import org.neogenesis.web.model.Gene;
import org.neogenesis.web.model.GeneAlia;

public class NcbiDAO implements Serializable {

	private EUtilsServiceStub service;

	private boolean activated = true;
	private String email = "ffumarola@di.uniba.it";
	private String tool = "Neo-GeneSIS";
	private int batchSize;

	@PostConstruct
	public void init() {
		try {
			service = new EUtilsServiceStub();

		} catch (AxisFault ex) {
			Logger.getLogger(NcbiDAO.class.getName()).log(Level.SEVERE, null,
					ex);
			activated = false;
		}
	}

	public boolean isActivated() {
		return activated;
	}

	public int getBatchSize() {
		return batchSize;
	}

	public void setBatchSize(int size) {
		batchSize = size;
	}

	public Gene getGene(String id) throws RemoteException {
		Gene gene = null;

		// call NCBI ESummary utility
		EUtilsServiceStub.ESummaryRequest req = new EUtilsServiceStub.ESummaryRequest();

		req.setDb("Gene");
		req.setId(id);
		req.setEmail(email);
		req.setTool(tool);

		EUtilsServiceStub.ESummaryResult res = service.run_eSummary(req);

		DocSumType docSumType = res.getDocSum()[0];

		gene = extractGene(docSumType.getId(), docSumType.getItem());

		return gene;
	}

	public Gene getGeneBySymbol(String symbol) throws RemoteException {

		ESearchRequest searchReq = new ESearchRequest();
		searchReq.setUsehistory("true");
		searchReq.setDb("gene");
		searchReq.setTerm(symbol + " AND \"Homo sapiens\"[organism]");

		ESearchResult searchResp = service.run_eSearch(searchReq);

		if (searchResp.getIdList().getId().length == 0) {
			return null;
		}

		Gene gene = null;

		// iterate at maximum over the first five to get the NcbiGene
		String[] gotIds = searchResp.getIdList().getId();
		for (String id : gotIds) {
			gene = getGene(id);
			if (gene != null) {
				return gene;
			}
		}

		return gene;
	}

	public void retrieveGenesById(List<MetaProbe> metaProbes) {

		HashMap<String, Gene> mapIdGene = new HashMap<String, Gene>();

		int start = 0;

		while (start < metaProbes.size()) {
			// increment also start
			start = loadGenesById(metaProbes, start, batchSize, mapIdGene);
		}

		for (MetaProbe metaProbe : metaProbes) {
			Gene gene = mapIdGene.get(metaProbe.getGeneId());
			if (gene != null) {
				metaProbe.setGene(gene);
			} else {
				System.out.println("Gene not Found");
			}
		}
	}

	public void retrieveGenesBySymbol(List<MetaProbe> metaProbes) {

		HashMap<String, Gene> mapIdGene = new HashMap<String, Gene>();

		int start = 0;

		while (start < metaProbes.size()) {
			start = loadGeneBySymbol(metaProbes, start, batchSize, mapIdGene);
		}
		// log.log(Level.INFO, "queried element {0}", start);
	}

	private int loadGeneBySymbol(List<MetaProbe> metaRows, int start,
			int batchSize, HashMap<String, Gene> mapIdGene) {

		StringBuffer queryString = new StringBuffer();

		// compose the query
		int i = start;
		int end = start + batchSize;

		if (end > metaRows.size()) {
			end = metaRows.size();
		}

		int realCount = 0;

		String or = " OR ";
		for (i = start; i < end - 1; i++) {
			if (!mapIdGene.containsKey(metaRows.get(i).getGeneId())) {
				queryString.append(metaRows.get(i).getGeneSymbol() + or);
				realCount++;
			}
		}
		// delete the last or
		int startDel = queryString.length() - or.length();
		int endDel = queryString.length();
		queryString = queryString.delete(startDel, endDel);
		// append the
		queryString.append(metaRows.get(i).getGeneId()
				+ " AND \"Homo sapiens\"[organism]");

		ESearchRequest searchReq = new ESearchRequest();
		searchReq.setUsehistory("true");
		searchReq.setDb("gene");
		searchReq.setTerm(queryString.toString());

		// call the service
		try {
			Logger.getLogger(NcbiDAO.class.toString()).log(Level.INFO,
					"querying {0} genes", realCount);
			ESearchResult searchResp = service.run_eSearch(searchReq);


			IdListType resultId = searchResp.getIdList();
			String[] ids = resultId.getId();

			// for each gene in meta probe set the gene id
			// with the gene ids got from the web service
			int j = 0;
			for (i = start; i < end - 1; i++) {
				metaRows.get(i).setGeneId(ids[j]);
				j++;
			}
			
			loadGenesById(metaRows, start, batchSize, mapIdGene);

			// verify the pair geneId gene symbol
			for (i = start; i < end - 1; i++) {
				Gene g = metaRows.get(i).getGene();
				if (g == null)
					continue;
				
				if (!isTheSameGene(metaRows.get(i).getGeneSymbol(),g))
					metaRows.get(i).setGene(null);
				else{
					metaRows.get(i).setGeneSymbol(g.getGeneSymbol());
				}
			}

		} catch (RemoteException e) {
			Logger.getLogger(NcbiDAO.class.getName()).log(Level.WARNING, null,
					e);
		}
		return end;
	}

	private boolean isTheSameGene(String geneSymbol, Gene g) {
		
		if (geneSymbol.equalsIgnoreCase(g.getGeneSymbol()))
			return true;
		
		List<GeneAlia> geneAlias = g.getGeneAlias();
		for (GeneAlia geneAlia : geneAlias) {
			if (geneSymbol.equalsIgnoreCase(geneAlia.getGeneSymbol()))
				return true;
		}
		
		return false;
	}

	private int loadGenesById(List<MetaProbe> metaProbes, int start,
			int batchSize, HashMap<String, Gene> mapIdGene) {

		StringBuffer queryString = new StringBuffer();

		// compose the query
		int i = start;
		int end = start + batchSize;

		if (end > metaProbes.size()) {
			end = metaProbes.size();
		}

		int realCount = 0;

		for (i = start; i < end - 1; i++) {
			if (!mapIdGene.containsKey(metaProbes.get(i).getGeneId())) {
				queryString.append(metaProbes.get(i).getGeneId());
				queryString.append(",");
				realCount++;
			}

		}
		queryString.append(metaProbes.get(i).getGeneId());

		EUtilsServiceStub.ESummaryRequest req = new EUtilsServiceStub.ESummaryRequest();
		req.setDb("Gene");
		req.setId(queryString.toString());
		req.setEmail(email);
		req.setTool(tool);

		// call the service
		try {
			Logger.getLogger(NcbiDAO.class.toString()).log(Level.INFO,
					"querying {0} genes", realCount);
			EUtilsServiceStub.ESummaryResult res = service.run_eSummary(req);


			DocSumType[] list = res.getDocSum();
			Gene gene;

			for (DocSumType docSumType : list) {
				gene = extractGene(docSumType.getId(), docSumType.getItem());
				mapIdGene.put(docSumType.getId(), gene);
				// metaProbes.get(count).setGene(gene);
			}

		} catch (RemoteException e) {
			Logger.getLogger(NcbiDAO.class.getName()).log(Level.WARNING, null,
					e);
		}
		return end;
	}

	public List<String> getSeries(String terms, String organism)
			throws RemoteException {

		List<String> series = new ArrayList<String>();

		EUtilsServiceStub.ESearchRequest req = new EUtilsServiceStub.ESearchRequest();
		req.setDb("gds");
		req.setRetMax("5000");
		req.setUsehistory("y");
		req.setEmail(email);
		req.setTool(tool);
		req.setTerm(terms);

		EUtilsServiceStub.ESearchResult res = service.run_eSearch(req);

		EUtilsServiceStub.IdListType ids = res.getIdList();

		for (String id : ids.getId()) {
			System.out.println(id);
		}

		return series;
	}

	private Gene extractGene(String id, ItemType[] itemTypes) {
		Gene g = new Gene();
		g.setId(Integer.parseInt(id));

		for (ItemType itemType : itemTypes) {
			// get the item name
			String itemName = itemType.getName();

			if (itemName.equalsIgnoreCase("chrSort"))
				g.setChrSort(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("chrStart"))
				g.setChrStart(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("chromosome"))
				g.setChromosome(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("description"))
				g.setDescription(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("Name"))
				g.setGeneSymbol(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("mapLocation"))
				g.setMapLocation(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("nomenclatureStatus"))
				g.setNomenclatureStatus(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("orgName"))
				g.setOrgName(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("OtherDesignations"))
				g.setOtherDesignation(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("status")) {
				String status = itemType.getItemContent();
				if (status.length() > 0)
					g.setStatus(Integer.parseInt(status));
			} else if (itemName.equalsIgnoreCase("summary"))
				g.setSummary(itemType.getItemContent());
			else if (itemName.equalsIgnoreCase("taxId")) {
				String taxId = itemType.getItemContent();
				if (taxId.length() > 0)
					g.setTaxId(Integer.parseInt(taxId));
			} else if (itemName.equalsIgnoreCase("OtherAliases")) {
				String value = itemType.getItemContent();
				if (value != null) {
					String[] split = value.split(",");
					List<GeneAlia> alias = new ArrayList<GeneAlia>();
					for (String s : split) {
						GeneAlia geneAlia = new GeneAlia();
						geneAlia.setGene(g);
						geneAlia.setGeneSymbol(s.trim());
						alias.add(geneAlia);
					}
					g.setGeneAlias(alias);
				}
			} else if (itemName.equalsIgnoreCase("nomenclatureName")) {
				g.setNomenclatureName(itemType.getItemContent());
			}

		}

		return g;
	}

}
